Release 10.1A: OpenEdge Development:
Progress 4GL Handbook


Examples: Communicating between persistent procedures

In this section, you extend the test windows to see a couple of different ways persistent procedures can communicate with one another.

To add a fill-in field to the main window to locate and manipulate one of the Order windows:

(You’ll just add code to hide or view it alternately, just to show that you can access it.)

  1. Open h-OrderWin.w and add this statement to the main block to save off the Order Number in the procedure’s PRIVATE-DATA attribute:
  2. ASSIGN THIS-PROCEDURE:PRIVATE-DATA = STRING(OrderNum) 
           shipDate:BGCOLOR = dateColor(PromiseDate, ShipDate). 
    

  3. Open h-CustOrderWin6.w and add a fill-in to the window. Call it iOrderWin and give it the label Show/Hide Order.
  4. Define this LEAVE trigger for the new fill-in:
  5. DO: 
      DEFINE VARIABLE hProc AS HANDLE     NO-UNDO. 
      DEFINE VARIABLE hWin  AS HANDLE     NO-UNDO. 
      hProc = SESSION:FIRST-PROCEDURE. 
      DO WHILE VALID-HANDLE(hProc): 
          IF hProc:FILE-NAME = "OrderWin.w" AND  
             hProc:PRIVATE-DATA = iOrderWin:SCREEN-VALUE THEN 
          DO: 
              hWin = hProc:CURRENT-WINDOW. 
              hWin:HIDDEN = IF hWin:HIDDEN THEN NO ELSE YES. 
              LEAVE. 
          END. 
          hProc = hProc:NEXT-SIBLING. 
      END. 
    END. 
    

    This trigger block walks through the persistent procedure list managed through the SESSION handle. When it comes to an instance of h-OrderWin.w whose PRIVATE-DATA attribute matches the value in the fill-in, it saves off its CURRENT-WINDOW attribute, which is the handle of the procedure’s window. If the window is currently hidden it is viewed, and vice versa.

  6. Save the changes and test them out:
    1. Run h-CustOrderWin6.w.
    2. Click the Order Detail button for a few different Orders.
    3. Enter one of the Order numbers into the Save/Hide Order fill-in and tab out of it. The window displaying that Order should disappear, or reappear if it’s already hidden.

This is a simple example of how to use the SESSION procedure list to locate a procedure you’re interested in, and then either manipulate that procedure object or make a request of it in some way.

The next example goes in the opposite direction. Rather than locating one of the Order windows from the main window, you add code to the Order window procedure to request the handle of another procedure from the main window. It then uses that handle to reposition the other Order window. A new internal procedure in the main window provides an interface the Order window can use to get the handle it needs.

Using SOURCE-PROCEDURE to identify your caller

First, you’ll learn another useful built-in function. You’ve seen the THIS-PROCEDURE handle function, which holds the handle of the current procedure. Another similar built-in handle function is SOURCE-PROCEDURE. Whenever Progress executes a RUN statement, the SOURCE-PROCEDURE handle in the called procedure returns the procedure handle of the caller.

To use SOURCE-PROCEDURE to identify your caller:

  1. To capture the SOURCE-PROCEDURE when the Order window first starts up, add this definition to the Definitions section of h-OrderWin.w:
  2. /* Local Variable Definitions ---                       */ 
    DEFINE VARIABLE hSource AS HANDLE     NO-UNDO. 
    

  3. Add another line to its main block, to save off the value on start-up:
  4. ASSIGN THIS-PROCEDURE:PRIVATE-DATA = STRING(OrderNum) 
             hSource = SOURCE-PROCEDURE 
             shipDate:BGCOLOR = dateColor(PromiseDate, ShipDate). 
    

  5. Add a new fill-in to the window called iOtherOrder, with a Label of Other Order.
  6. Add a new button called BtnAlign, with a Label of Align Order.
  7. You can put a rectangle around them to group them if you want, like this:

  8. Code this CHOOSE trigger for BtnAlign:
  9. DO: 
      DEFINE VARIABLE hOtherWin AS HANDLE     NO-UNDO. 
      DEFINE VARIABLE hWindow   AS HANDLE     NO-UNDO. 
      RUN fetchOrderWin IN hSource  
          (INPUT iOtherOrder:SCREEN-VALUE, 
           OUTPUT hOtherWin). 
      IF VALID-HANDLE(hOtherWin) THEN 
      DO: 
          ASSIGN hWindow = hOtherWin:CURRENT-WINDOW 
                 hWindow:ROW = THIS-PROCEDURE:CURRENT-WINDOW:ROW + 1 
                 hWindow:COLUMN = THIS-PROCEDURE:CURRENT-WINDOW:COLUMN + 3. 
      END. 
    END. 
    

    This code runs an internal procedure in its source (the main window), which takes an Order number as input and returns as output the handle of the window with that Order number in it. The code then uses the CURRENT-WINDOW attribute of that other window and the CURRENT-WINDOW attribute of THIS-PROCEDURE to align the other window just below and to the right of this one.

  10. In h-CustOrderWin6.w, add another variable to the Definitions section:
  11. DEFINE VARIABLE cOrderNumbers AS CHARACTER   NO-UNDO. 
    

    This variable will hold a list of all the Order numbers the procedure opens up windows for.

  12. Add a statement to the BtnOrder (Order Detail) button to save off the Order number of each window as it’s created, in addition to its procedure handle:
  13. DO: 
        DEFINE VARIABLE hOrder AS HANDLE     NO-UNDO. 
        RUN h-OrderWin.w PERSISTENT SET hOrder (BUFFER Order). 
        ASSIGN cOrderHandles = cOrderHandles +  
            (IF cOrderHandles NE "" THEN "," ELSE "") + STRING(hOrder) 
                cOrderNumbers = cOrderNumbers +  
          (IF cOrderNumbers NE "" THEN "," ELSE "") + STRING(Order.OrderNum). 
    END. 
    

  14. Define the new fetchOrderWin internal procedure in h-CustOrderWin6.w:
  15. /*--------------------------------------------------------------------- 
      Purpose:   Returns the procedure handle to the instance of h-OrderWin.w 
                 that displays the requested Order Number, 
      Notes:        
    ---------------------------------------------------------------------*/ 
    DEFINE INPUT  PARAMETER pcOrderNum AS CHARACTER  NO-UNDO. 
    DEFINE OUTPUT PARAMETER phOrderWin AS HANDLE     NO-UNDO. 
    DO iHandle = 1 TO NUM-ENTRIES(cOrderNumbers): 
        IF ENTRY(iHandle, cOrderNumbers) = pcOrderNum THEN 
        DO: 
            phOrderWin = WIDGET-HANDLE(ENTRY(iHandle,cOrderHandles)). 
            RETURN. 
        END. 
    END. 
    END PROCEDURE. 
    

    This code acts as an API call that other procedures like the instances of h-OrderWin can run to obtain data from the main window. It looks through the list of Order numbers and, when it finds the one that was requested, it passes back as output the matching procedure handle for that Order.

To test out this latest change:

  1. Run h-CustOrderWin6.w.
  2. Open a couple of Order Detail windows.
  3. In one of them, enter the Order number displayed in the other, and click the Align Order button (or tab out of the fill-in and press ENTER to do the same thing.) The windows should align like this:

Why did you use a button with a CHOOSE trigger instead of just defining a LEAVE trigger for the fill-in? In this case, without the button the fill-in would be the only enabled object in the window, so its LEAVE trigger would not fire. You need at least two enabled objects in a window in order to leave one of them. So the button gives you something to tab into and click.

This latest example shows you in simple terms how you can provide an API in a procedure that other persistent procedures can use to get information from, or to invoke actions in, and a couple of different ways to obtain the handles of other running procedures you’re interested in.

Now that you have learned the difference between non-persistent and persistent procedures, and how the stack of procedure calls in a typical older Progress application is turned into something more flexible with persistent procedures, it’s time to learn about a programming concept that was very important in those older applications, but which you won’t use often in new applications.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095